package de.jadehs.cosima;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.EList;
import org.jgrapht.DirectedGraph;

import de.jadehs.cosima.converters.TransitionsTableToDirectedGraphConverter;
import de.jadehs.cosima.util.PrettyPrintUtil;

public class CosimaUtil {
	public static final Character EPSILON = new Character('\u03F5');

	public static void execute(Cosima cosima) {
		Machine machine = cosima.getMachine();
		Program program = cosima.getPrograms().get(0);

		if (program instanceof TransitionsTable) {
			execute(machine, (TransitionsTable) program);
		}
	}

	public static <FROM, TO> TO convert(Object obj, FROM from, TO to) {

		return (TO) null;
	}

	public static State execute(Machine machine,
			DirectedGraph<State, Transition> graph) {
		return null;
	}

	public static State execute(Machine machine, TransitionsTable table) {
		Tape tape = table.getTape();
		tape.reset();

		State startState = table.getTransitions().get(0).getStart();
		table.setActiveState(startState);

		Character c;

		while ((c = tape.consumeInput()) != null) {
			List<Transition> transitions = findTransitions(table,
					table.getActiveState());

			boolean found = false;

			for (Transition t : transitions) {
				if (evaluateStartCondition(t, c)) {
					found = true;
					table.setActiveState(t.getEnd());
					break;
				}
			}

			if (!found) {
				State state = CosimaFactory.eINSTANCE.createState();
				state.setType(StateType.UNDEF);
				state.setName("No transition to match: processed input=" + c
						+ ", activeState=" + table.getActiveState().getName());
				table.setActiveState(state);
				break;
			}
		}

		return table.getActiveState();
	}

	public static boolean evaluateStartCondition(Transition t, Character c) {
		return evaluate(t.getStartInstructions(), c);
	}

	private static boolean evaluate(List<Instruction> instructions, Character c) {
		boolean allOK = true;

		for (Instruction instr : instructions) {
			if (instr instanceof ReadFromTape) {
				allOK = allOK
						&& c.charValue() == ((ReadFromTape) instr).getSymbol();
			}

			if (!allOK) {
				break;
			}
		}

		return allOK;
	}

	private static List<Transition> findTransitions(TransitionsTable dtable,
			State key) {
		List<Transition> ret = new ArrayList<Transition>();

		for (Transition t : dtable.getTransitions()) {
			if (t.getStart() == key) {
				ret.add(t);
			}
		}

		return ret;
	}

	public static State getStartState(StatesRegister reg) {
		for (State s : reg.getStates()) {
			if (s.isStart()) {
				return s;
			}
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public static <T> T adapt(Object obj, Class<T> t, Object... params) {
		return (T) Platform.getAdapterManager().getAdapter(obj, t);
	}

	public static List<Character> determineConcreteAlphabet(
			TransitionsTable table) {
		return table.getTape().getAlphabet();
	}

	public static boolean isNFA(TransitionsTable f) {
		DirectedGraph<State, Transition> graph = TransitionsTableToDirectedGraphConverter.INSTANCE
				.convert(f);
		List<Character> alphabet = CosimaUtil.determineConcreteAlphabet(f);

		for (State s : graph.vertexSet()) {
			Set<Transition> tranistions = graph.edgesOf(s);

			Map<Character, Boolean> ambigous = new HashMap<Character, Boolean>();

			for (Transition t : tranistions) {
				for (Character c : alphabet) {
					boolean canEnter = CosimaUtil.evaluate(
							t.getStartInstructions(), c);
					Boolean entered = ambigous.get(c);

					if (entered == null) {
						entered = false;
						ambigous.put(c, canEnter);
					}

					if (canEnter && entered) {
						return true;
					}
				}
			}
		}

		return false;
	}

	public static boolean isEpsNFA(TransitionsTable table) {
		return determineConcreteAlphabet(table).contains(EPSILON);
	}

	public static boolean hasFinal(List<State> states) {
		for (State s : states) {
			if (s.getType() == StateType.END) {
				return true;
			}
		}

		return false;
	}

	public static String createConcatName(List<State> states) {
		if (states.isEmpty()) {
			return "{}";
		}

		StringBuffer newStateName = new StringBuffer("{");

		for (State state : states) {
			newStateName.append(state.getName()).append(",");
		}

		newStateName.deleteCharAt(newStateName.length() - 1);
		newStateName.append("}");
		return newStateName.toString();
	}
}
